#pragma once

/*
//多态的应用

    将日志信息（字段）进行格式化输出 - 字符串
    pattern成员：保存⽇志输出的格式字符串。
◦ %d    ⽇期
◦ %T    缩进
◦ %t    线程id
◦ %p    ⽇志等级
◦ %c    ⽇志器名称
◦ %f    ⽂件名
◦ %l    ⾏号
◦ %m    ⽇志消息
◦ %n    换⾏

    用户可以自定义日志输出的格式，该模块只是在对应位置放入字段，这里是调用各个子格式的函数来完成的【父类指针指向不同的子类对象】
    (保证调用顺序)
*/
/*
    子格式：对于不用的字段有不同的函数
    std::vector<FormatItem::ptr> items成员：⽤于按序保存格式化字符串对应的⼦格式化对象。
    FormatItem类主要负责⽇志消息⼦项的获取及格式化。其包含以下⼦类

    //格式化子项功能 ： 从日志消息中，取出对应的字段填充到一段缓冲区中
• MsgFormatItem ：  表⽰要从LogMsg中取出有效⽇志数据
• LevelFormatItem ：表⽰要从LogMsg中取出⽇志等级
• NameFormatItem ： 表⽰要从LogMsg中取出⽇志器名称
• ThreadFormatItem ：表⽰要从LogMsg中取出线程ID
• TimeFormatItem ：  表⽰要从LogMsg中取出时间戳并按照指定格式进⾏格式化
• CFileFormatItem ：表⽰要从LogMsg中取出源码所在⽂件名
• CLineFormatItem ：表⽰要从LogMsg中取出源码所在⾏号
• TabFormatItem ：  表⽰⼀个制表符缩进
• NLineFormatItem ：表⽰⼀个换⾏
• OtherFormatItem ：表⽰⾮格式化的原始字符串
*/

/*
    抽象出一个格式化子项基类
    基于基类，派生出不同字段的子项类

*/
#include "2_level.hpp"
#include "3_msg.hpp"
#include <iostream>
#include <ctime>
#include <vector>
#include <cassert>
#include <sstream>

namespace senlog
{
    // 1. 抽象出基类
    class FormatItem
    {
    public:
        using ptr = std::shared_ptr<FormatItem>;
        virtual void format(std::ostream &out, const LogMsg &msg) = 0;
    };
    // 2. 构建多态
    class MsgFormatItem : public FormatItem
    {
    public:
        virtual void format(std::ostream &out, const LogMsg &msg) override
        {
            out << msg._payload;//有效载荷 不用特殊处理，直接输出
        }
    };
    class LevelFormatItem : public FormatItem
    {
    public:
        virtual void format(std::ostream &out, const LogMsg &msg) override
        {
            out << LogLevel::tostring(msg._level);
        }
    };
    class TimeFormatItem : public FormatItem // 时间是有它的子格式的
    {
    private:
        std::string _time_fmt; // 时间是有默认格式的： %H:%M:%S 时分秒
                               // 我们要将这个格式的时间转换为字符串 struct localtime{} 、strftime()
    public:
        TimeFormatItem(const std::string &time_fmt = "%H:%M:%S")
            : _time_fmt(time_fmt)
        {}
        virtual void format(std::ostream &out, const LogMsg &msg) override
        {
            struct tm t;
            localtime_r(&msg._ctime, &t); // ?
            char tmp[32] = {0};
            strftime(tmp, 31, _time_fmt.c_str(), &t); // ?
            out << tmp;
        }
    };
    class FileNameFormatItem : public FormatItem
    {
    public:
        virtual void format(std::ostream &out, const LogMsg &msg) override
        {
            out << msg._filename;
        }
    };
    class LineNameFormatItem : public FormatItem
    {
    public:
        virtual void format(std::ostream &out, const LogMsg &msg) override
        {
            out << msg._line;
        }
    };
    class TidNameFormatItem : public FormatItem
    {
    public:
        virtual void format(std::ostream &out, const LogMsg &msg) override
        {
            out << msg._tid;
        }
    };
    class LoggerNameFormatItem : public FormatItem
    {
    public:
        virtual void format(std::ostream &out, const LogMsg &msg) override
        {
            out << msg._loggername;
        }
    };
    class TabNameFormatItem : public FormatItem // 制表符 tab
    {
    public:
        virtual void format(std::ostream &out, const LogMsg &msg) override
        {
            out << "\t";
        }
    };
    class NewLineNameFormatItem : public FormatItem // 换行 \n
    {
    public:
        virtual void format(std::ostream &out, const LogMsg &msg) override
        {
            out << "\n";
        }
    };
    class OtherNameFormatItem : public FormatItem // 换行 \n
    {
    private:
        std::string _str;

    public:
        OtherNameFormatItem(const std::string &str)
            : _str(str)
        {
        }
        virtual void format(std::ostream &out, const LogMsg &msg) override
        {
            out << _str;
        }
    };

    /*
        %d 日期 包含子格式
    */
    class Formatter
    {
        // 格式化器
    private:
        std::string _pattern;                // 格式化规则字符串
        std::vector<FormatItem::ptr> _items; // 为什么不是类
    public:
        using ptr = std::shared_ptr<Formatter>;
        Formatter(const std::string &pattern = "[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n")
            : _pattern(pattern)
        {
            // 解析
            assert(parsePattern()); // 格式必须正确
        }
        void format(std::ostream &out, const LogMsg &msg)
        {
            // 按照传入格式的顺序，逐个调用子项目信息
            for (auto &it : _items)
            {
                it->format(out, msg); // 遍历vector数组, 通过基类指针FormatItem* 指针指向不同子类对象
            }
        }
        std::string format(const LogMsg &msg) // 使用重载，达到不直接向显示器而是向一个缓冲区中存入字符串
        {
            std::stringstream ss; // 具有能被 << >> 直接操作的流式能力。且是内存级的缓冲区，减少磁盘IO
            format(ss, msg);
            return ss.str();
        }
    
    private:
        bool parsePattern() // ***解析接口（难）***
        {
            // 该函数 逐个解析pattern格式
            // 将key与val传入createItem方法，返回一个个的各子项对象
            // 并插入至vector数组里等待调用
            std::vector<std::pair<std::string, std::string>> fmt_order;
            int pos = 0;          // 从0开始分解字符串
            std::string key, val; // 定义存储位置
            while (pos < _pattern.size())
            {
                if (_pattern[pos] != '%') // 说明是普通字符串，而不是格式化字符
                {
                    val.push_back(_pattern[pos++]);
                    continue; // 直接判断下一个
                }
                if ((pos + 1) < _pattern.size() && _pattern[pos + 1] == '%')
                {
                    // 说明为 %% 转义为一个 %
                    val.push_back('%');
                    pos += 2;
                    continue;
                }
                if (val.empty() == false) // 确定为普通字符串
                {
                    fmt_order.push_back(std::make_pair("", val)); // 直接存入_items数组
                    val.clear();                                  // 不要忘记清空，val还要继续
                }

                // 是一个格式化字符, 目前指向了 % 位置
                pos++; // 调整到key字符处
                if (pos == _pattern.size())
                {
                    // 走到了末尾
                    std::cout << "% 之后没有任何格式化字符" << std::endl;
                    return false;
                }
                key.push_back(_pattern[pos]); // 存下key，如果有子项还需要存下子项val
                pos++;
                if (pos < _pattern.size() && _pattern[pos] == '{')
                {          // 说明有子项
                    pos++; // 进入子项
                    while (pos < _pattern.size() && _pattern[pos] != '}')
                    {
                        val.push_back(_pattern[pos++]);
                    }
                    if (pos == _pattern.size())
                    {
                        std::cout << "_pattern format err" << std::endl;
                        return false;
                    }
                    pos++; //这里表示{}中的内容全部加入val，需要将pos再往后一位，判断后续的pattern
                }
                fmt_order.push_back(std::make_pair(key, val));
                key.clear();
                val.clear();
            }

            //到这里，说明已经将pattern字符串中的字段进行了分离，并存储到了fmt_order中
            //所以就可以直接根据 createItem 与fmt_order中的key val进行初始化子项类
            for(auto &it : fmt_order)
            {
                _items.push_back(createItem(it.first, it.second));
            }
            return true;
        }

        FormatItem::ptr createItem(const std::string &key, const std::string &val)
        {
            if (key == "d")
                return std::make_shared<TimeFormatItem>(val);
            if (key == "t")
                return std::make_shared<TidNameFormatItem>();
            if (key == "c")
                return std::make_shared<LoggerNameFormatItem>();
            if (key == "f")
                return std::make_shared<FileNameFormatItem>();
            if (key == "l")
                return std::make_shared<LineNameFormatItem>();
            if (key == "p")
                return std::make_shared<LevelFormatItem>();
            if (key == "T")
                return std::make_shared<TabNameFormatItem>();
            if (key == "m")
                return std::make_shared<MsgFormatItem>();
            if (key == "n")
                return std::make_shared<NewLineNameFormatItem>();
            if(key == "") 
                return std::make_shared<OtherNameFormatItem>(val);
            std::cout << "没有对应的格式化字符" << key << std::endl;
            abort();
            return FormatItem::ptr();
        }
    };

};